package com.joysuch.wwyt.bp.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.UnmodifiableIterator;
import com.joysuch.wwyt.bp.bean.risk.*;
import com.joysuch.wwyt.bp.entity.BpDepart;
import com.joysuch.wwyt.bp.entity.risk.BpTeam;
import com.joysuch.wwyt.bp.entity.risk.BpTeamRiskJudge;
import com.joysuch.wwyt.bp.entity.risk.BpTeamSafeDevice;
import com.joysuch.wwyt.bp.entity.risk.BpTeamSafeDeviceDetail;
import com.joysuch.wwyt.bp.enums.RiskJudgeStates;
import com.joysuch.wwyt.bp.repository.*;
import com.joysuch.wwyt.bp.service.BpTeamService;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.PageDataUtils;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.support.Context;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author ZhangDong
 * @Date 2020/3/4 12:02
 */
@Service
@Slf4j
@Transactional
public class BpTeamServiceImpl implements BpTeamService {

    @Autowired
    private BpTeamRepository bpTeamRepository;
    @Autowired
    private BpTeamRiskJudgeRepository bpTeamRiskJudgeRepository;
    @Autowired
    private BpTeamSafeDeviceRepository bpTeamSafeDeviceRepository;
    @Autowired
    private BpTeamSafeDeviceDetailRepository bpTeamSafeDeviceDetailRepository;
    @Autowired
    private BpDepartDao bpDepartDao;
    @Autowired
    private BaseUserDao baseUserDao;

    @Override
    public ResultBean save(BpTeamBean bpTeamBean) {
        Long currentDepartId = getCurrentDepartId();
        /*LocalDate reportDate = bpTeamBean.getReportDate();
        Long teamDepartId = bpTeamBean.getTeamDepartId();*/

        Long id = bpTeamBean.getId();
        BpTeam bpTeam;
        if (id == null) {
            //放开，可以多次
            /*//新增：同一部门，同一个上报日期只能一条
            List<BpTeam> tempList = bpTeamRepository.findByReportDateAndTeamDepartId(reportDate, teamDepartId);
            if (!CollectionUtils.isEmpty(tempList)) {
                return ResultBean.fail(101, "同一部门同一天只能上报一次");
            }*/
            bpTeam = new BpTeam();
            bpTeam.setReleaseStatus(RiskJudgeStates.WAIT_COMMIT.getState());
        } else {
            bpTeam = bpTeamRepository.findById(id).orElse(new BpTeam());
            //修改权限：只有本班组
            if (currentDepartId != null && !currentDepartId.equals(bpTeam.getTeamDepartId())) {
                return ResultBean.fail(101, "没有权限");
            }
            /*//修改时日期已存在
            if (!reportDate.isEqual(bpTeam.getReportDate())) {
                List<BpTeam> tempList = bpTeamRepository.findByReportDateAndTeamDepartId(reportDate, teamDepartId);
                if (!CollectionUtils.isEmpty(tempList)) {
                    return ResultBean.fail(101, "同一部门同一天只能上报一次");
                }
            }*/
        }

        //安全风险研判表，结果如果是不涉及，风险等级不必填
        List<BpTeamSafeRiskBean> riskBeanList = bpTeamBean.getRiskBeanList();
        for (BpTeamSafeRiskBean bpTeamSafeRiskBean : riskBeanList) {
            List<BpTeamSafeRiskDetailBean> detailBeanList = bpTeamSafeRiskBean.getDetailBeanList();
            for (BpTeamSafeRiskDetailBean detailBean : detailBeanList) {
                if (!"不涉及".equals(detailBean.getResult()) && StringUtils.isEmpty(detailBean.getRiskLevel())) {
                    return ResultBean.fail(101, "结果除不涉及，风险等级不能为空！");
                }
            }
        }

        BeanUtils.copyProperties(bpTeamBean, bpTeam);
        //特种作业
        try {
            bpTeam.setSpecialWork(JSON.toJSONString(bpTeamBean.getSpecialBeanList()));
        } catch (Exception e) {
            return ResultBean.fail(101, "特种作业数据格式不正确");
        }
        BpTeam bpTeamSave = bpTeamRepository.save(bpTeam);
        Long bpTeamId = bpTeamSave.getId();

        Set<Long> tempSet = new HashSet<>();
        if (id != null) {
            List<BpTeamRiskJudge> riskJudges = bpTeamRiskJudgeRepository.findByTeamId(id);
            tempSet = riskJudges.stream().map(r -> r.getId()).collect(Collectors.toSet());
        }
        //风险研判
        List<BpRiskJudgeBean> judgeBeanList = bpTeamBean.getJudgeBeanList();
        if (CollectionUtils.isEmpty(judgeBeanList)) {
            log.info("风险研判，班组id：" + currentDepartId + " 风险研判judgeBeanList为空");
        }
        if (!CollectionUtils.isEmpty(judgeBeanList)) {
            List<BpTeamRiskJudge> needSaveList = Lists.newArrayList();
            for (BpRiskJudgeBean bpRiskJudgeBean : judgeBeanList) {
                Long riskJudgeId = bpRiskJudgeBean.getRiskJudgeId();
                BpTeamRiskJudge bpTeamRiskJudge;
                if (riskJudgeId == null) {
                    bpTeamRiskJudge = new BpTeamRiskJudge();
                } else {
                    bpTeamRiskJudge = bpTeamRiskJudgeRepository.findById(riskJudgeId).orElse(new BpTeamRiskJudge());
                }
                BeanUtils.copyProperties(bpRiskJudgeBean, bpTeamRiskJudge);
                bpTeamRiskJudge.setTeamId(bpTeamId);
                needSaveList.add(bpTeamRiskJudge);
            }

            bpTeamRiskJudgeRepository.saveAll(needSaveList);
        }
        //删除的取差集
        Set<Long> collect = judgeBeanList.stream().filter(r -> r.getRiskJudgeId() != null).map(r -> r.getRiskJudgeId()).collect(Collectors.toSet());
        Sets.SetView<Long> difference = Sets.difference(tempSet, collect);
        UnmodifiableIterator<Long> iterator = difference.iterator();
        while (iterator.hasNext()) {
            Long next = iterator.next();
            bpTeamRiskJudgeRepository.deleteById(next);
        }

        //安全风险研判表

        if (!CollectionUtils.isEmpty(riskBeanList)) {
            for (BpTeamSafeRiskBean bpTeamSafeRiskBean : riskBeanList) {
                String deviceName = bpTeamSafeRiskBean.getDeviceName();
                List<BpTeamSafeRiskDetailBean> detailBeanList = bpTeamSafeRiskBean.getDetailBeanList();
                //设备
                Long safeRiskId = bpTeamSafeRiskBean.getSafeRiskId();
                BpTeamSafeDevice bpTeamSafeDevice;
                if (safeRiskId == null) {
                    bpTeamSafeDevice = new BpTeamSafeDevice();
                } else {
                    bpTeamSafeDevice = bpTeamSafeDeviceRepository.findById(safeRiskId).orElse(new BpTeamSafeDevice());
                }
                bpTeamSafeDevice.setTeamId(bpTeamId);
                bpTeamSafeDevice.setDeviceName(deviceName);
                BpTeamSafeDevice save = bpTeamSafeDeviceRepository.save(bpTeamSafeDevice);
                Long safeDeviceId = save.getId();

                //设备详情
                for (BpTeamSafeRiskDetailBean detailBean : detailBeanList) {
                    Long contentId = detailBean.getContentId();
                    BpTeamSafeDeviceDetail bpTeamSafeDeviceDetail;
                    if (contentId == null) {
                        bpTeamSafeDeviceDetail = new BpTeamSafeDeviceDetail();
                    } else {
                        bpTeamSafeDeviceDetail = bpTeamSafeDeviceDetailRepository.findById(contentId)
                                .orElse(new BpTeamSafeDeviceDetail());
                    }
                    BeanUtils.copyProperties(detailBean, bpTeamSafeDeviceDetail);
                    bpTeamSafeDeviceDetail.setDeviceId(safeDeviceId);
                    bpTeamSafeDeviceDetailRepository.save(bpTeamSafeDeviceDetail);
                }
            }
        }
        return ResultBean.defaultSuccessResult();
    }

    @Override
    public ResultBean getById(Long id) {
        //权限限制：只有本班组或上级部门
        //Long currentDepartId = getCurrentDepartId();

        BpTeam bpTeam = bpTeamRepository.findById(id).orElse(null);
        if (bpTeam == null) {
            log.warn("BpTeam表数据丢失，id:{}", id);
            return ResultBean.fail(101, "系统繁忙");
        }
//        if ((currentDepartId != null && !currentDepartId.equals(bpTeam.getTeamDepartId())
////                && !currentDepartId.equals(bpTeam.getHigherDepartId()))) {
////            return ResultBean.fail(101, "没有权限");
////        }

        BpTeamDto dto = new BpTeamDto();
        BeanUtils.copyProperties(bpTeam, dto);
        //特种作业
        String specialWork = bpTeam.getSpecialWork();
        if (!StringUtils.isEmpty(specialWork)) {
            dto.setSpecailBeanList(JSON.parseArray(specialWork, BpTeamSpecialBean.class));
        }
        //风险研判
        List<BpTeamRiskJudge> riskJudgeList = bpTeamRiskJudgeRepository.findByTeamId(id);
        dto.setJudgeBeanList(riskJudgeList.stream().map(r -> new BpRiskJudgeBean(r.getId(), r.getExistRiskName(), r.getSafeMethod(),
                r.getRiskLevel(), r.getExistAccidentRiskOrNot())).collect(Collectors.toList()));

        //安全承诺
        dto.setPromiseUserName(baseUserDao.getBaseUserNameById(dto.getPromiseUserId()));
        dto.setReportUserName(baseUserDao.getBaseUserNameById(dto.getReportUserId()));
        dto.setHigherDepartName(bpDepartDao.findDepartNameById(dto.getHigherDepartId()));
        dto.setTeamDepartName(bpDepartDao.findDepartNameById(dto.getTeamDepartId()));

        //安全风险研判表
        List<BpTeamSafeRiskBean> riskBeanList = Lists.newArrayList();
        List<BpTeamSafeDevice> deviceList = bpTeamSafeDeviceRepository.findByTeamId(id);
        for (BpTeamSafeDevice device : deviceList) {
            BpTeamSafeRiskBean bpTeamSafeRiskBean = new BpTeamSafeRiskBean();
            bpTeamSafeRiskBean.setDeviceName(device.getDeviceName());
            Long deviceId = device.getId();
            bpTeamSafeRiskBean.setSafeRiskId(deviceId);
            if (deviceId == null) {
                log.warn("BpTeamSafeDevice 数据有误，缺id，deviceName:{}" + device.getDeviceName());
                continue;
            }
            List<BpTeamSafeDeviceDetail> deviceDetailList = bpTeamSafeDeviceDetailRepository.findByDeviceId(deviceId);
            List<BpTeamSafeRiskDetailBean> detailBeanList = Lists.newArrayList();
            if (!CollectionUtils.isEmpty(deviceDetailList)) {
                detailBeanList = deviceDetailList.stream().map(r -> {
                    BpTeamSafeRiskDetailBean bpTeamSafeRiskDetailBean = new BpTeamSafeRiskDetailBean();
                    BeanUtils.copyProperties(r, bpTeamSafeRiskDetailBean);
                    bpTeamSafeRiskDetailBean.setContentId(r.getId());
                    return bpTeamSafeRiskDetailBean;
                }).collect(Collectors.toList());
            }
            bpTeamSafeRiskBean.setDetailBeanList(detailBeanList);
            riskBeanList.add(bpTeamSafeRiskBean);
        }
        dto.setRiskBeanList(riskBeanList);
        return ResultBean.success(dto);
    }

    private Long getCurrentDepartId() {
        return Context.getCurrentDepartId();
    }

    /**
     * @param pageable
     * @param reportDate 上报日期
     * @param flag       0 班组级查询; 1 部门级查询(点上报查询的内容) 查询班组级已上报的; 2 班组级审核
     * @return
     */
    @Override
    public ResultBean page(Pageable pageable, LocalDate reportDate, int flag) {
        //查询限制：flag==0 只有本班组; flag==1 查询部门下所有班组; flag==2 只有上级部门+等待审批
        if (flag != 0 && flag != 1 && flag != 2) {
            return ResultBean.fail(101, "flag传值错误");
        }
        Long currentDepartId = getCurrentDepartId();
        pageable = PageDataUtils.addOrderByDesc(pageable, "id");
        Specification<BpTeam> querySpecifi = new Specification<BpTeam>() {
            @Override
            public Predicate toPredicate(Root<BpTeam> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
                List<Predicate> predicates = new ArrayList<>();
                if (flag == 0 && !Context.getCurrentUser().username.equals("admin")) {
                    predicates.add(cb.or(cb.equal(root.get("teamDepartId"), currentDepartId)));
                } else if (flag == 1) {
                    predicates.add(cb.equal(root.get("reportDate"), LocalDate.now()));
                    if (!Context.getCurrentUser().username.equals("admin")) {
                        predicates.add(cb.equal(root.get("higherDepartId"), currentDepartId));
                    }
                    predicates.add(cb.equal(root.get("releaseStatus"), RiskJudgeStates.REPORTED.getState()));
                } else if (flag == 2){
                    if (!Context.getCurrentUser().username.equals("admin")) {
                        predicates.add(cb.equal(root.get("higherDepartId"), currentDepartId));
                    }
                    CriteriaBuilder.In<Object> in = cb.in(root.get("releaseStatus"));
                    in.value(RiskJudgeStates.WAIT_APPROVE.getState());
                    in.value(RiskJudgeStates.REPORTED.getState());
                    in.value(RiskJudgeStates.NOT_APPROVE.getState());
                    predicates.add(in);
                }
                if (flag != 1 && reportDate != null) {
                    predicates.add(cb.equal(root.get("reportDate"), reportDate));
                }
                return cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        Page<BpTeam> all = bpTeamRepository.findAll(querySpecifi, pageable);
        List<Long> userIdList = all.getContent().stream().map(r -> r.getReportUserId()).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        Map<Long, String> userMap = baseUserDao.findAllById(userIdList).stream().collect(Collectors.toMap(r -> r.getId(), r -> r.getRealName()));
        List<Long> departIdList = all.getContent().stream().map(r -> r.getTeamDepartId()).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        Map<Long, String> departMap = bpDepartDao.findAllById(departIdList).stream().collect(Collectors.toMap(r -> r.getId(), r -> r.getName()));
        for (BpTeam bpTeam : all) {
            bpTeam.setTeamDepartName(departMap.getOrDefault(bpTeam.getTeamDepartId(), ""));
            bpTeam.setReportUserName(userMap.getOrDefault(bpTeam.getReportUserId(), ""));
        }
        return ResultBean.pageData(all.getContent(), all.getTotalElements());
    }

    @Override
    public ResultBean submit(Long id) {
        BpTeam bpTeam = bpTeamRepository.findById(id).orElse(null);
        if (bpTeam == null) {
            log.warn("BpTeam表数据丢失，id:{}", id);
            return ResultBean.fail(101, "系统繁忙");
        }

        //权限控制：只有本班组的可提交
        Long currentDepartId = getCurrentDepartId();
        if (currentDepartId != null && !currentDepartId.equals(bpTeam.getTeamDepartId())) {
            return ResultBean.fail(101, "没有权限");
        }
        //新需求，审批不通过的也可以继续提交
        String releaseStatus = bpTeam.getReleaseStatus();
        if (!RiskJudgeStates.WAIT_COMMIT.getState().equals(releaseStatus)
                && !RiskJudgeStates.NOT_APPROVE.getState().equals(releaseStatus)) {
            return ResultBean.fail(101, "当前记录已提交，不可重复提交");
        }
        bpTeam.setReleaseStatus(RiskJudgeStates.WAIT_APPROVE.getState());
        bpTeamRepository.save(bpTeam);
        return ResultBean.defaultSuccessResult();
    }

    @Override
    public ResultBean delete(Long id) {
        BpTeam bpTeam = bpTeamRepository.findById(id).orElse(null);
        if (bpTeam == null) {
            log.warn("BpTeam表数据丢失，id:{}", id);
            return ResultBean.fail(101, "系统繁忙");
        }

        //权限控制:只有本班组或上级部门可删除
        Long currentDepartId = getCurrentDepartId();
        if (currentDepartId != null && !currentDepartId.equals(bpTeam.getTeamDepartId())) {
            return ResultBean.fail(101, "没有权限");
        }

        bpTeamRepository.deleteById(id);
        bpTeamRiskJudgeRepository.deleteByTeamId(id);
        List<BpTeamSafeDevice> deviceList = bpTeamSafeDeviceRepository.findByTeamId(id);
        for (BpTeamSafeDevice bpTeamSafeDevice : deviceList) {
            bpTeamSafeDeviceDetailRepository.deleteByDeviceId(bpTeamSafeDevice.getId());
        }
        bpTeamSafeDeviceRepository.deleteByTeamId(id);
        return ResultBean.defaultSuccessResult();
    }

    @Override
    public ResultBean verify(Long id, int flag) {
        BpTeam bpTeam = bpTeamRepository.findById(id).orElse(null);
        if (bpTeam == null) {
            log.warn("BpTeam表数据丢失，id:{}", id);
            return ResultBean.fail(101, "系统繁忙");
        }
        //审核权限限制：只有指定的上级部门可审批
        Long currentDepartId = getCurrentDepartId();
        if (currentDepartId != null && !currentDepartId.equals(bpTeam.getHigherDepartId())) {
            return ResultBean.fail(101, "没有权限");
        }

        if (!RiskJudgeStates.WAIT_APPROVE.getState().equals(bpTeam.getReleaseStatus())) {
            return ResultBean.fail(101, "当前状态不能审批");
        }
        bpTeam.setReleaseStatus(flag == 1 ? RiskJudgeStates.REPORTED.getState() : RiskJudgeStates.NOT_APPROVE.getState());
        bpTeamRepository.save(bpTeam);
        return ResultBean.defaultSuccessResult();
    }

    @Override
    public ResultBean getDepart(int flag) {
        Long currentDepartId = getCurrentDepartId();
        BpDepart bpDepart = bpDepartDao.findById(currentDepartId).orElse(null);
        if (flag == 0) {
            return ResultBean.success(bpDepart);
        } else if (flag == 1) {
            if (bpDepart != null && bpDepart.getParentId() != null) {
                return ResultBean.success(bpDepartDao.findById(bpDepart.getParentId()).orElse(null));
            } else {
                return ResultBean.success(null);
            }
        }
        return ResultBean.success(bpDepartDao.findById(getCurrentDepartId()).orElse(null));
    }

    /**
     * 获取当前班组最近的一次记录
     *
     * @return
     */
    @Override
    public ResultBean getLastTeam() {
        BpTeam bpTeam = bpTeamRepository.findLastTeam(Context.getCurrentDepartId(),
                Context.getCurrentTenantId(), Context.getCompanyCode());
        if (bpTeam != null) {
            ResultBean resultBean = getById(bpTeam.getId());
            BpTeamDto dto = (BpTeamDto) resultBean.getData();
            List<BpTeamSafeRiskBean> riskBeanList = dto.getRiskBeanList();
            if (!CollectionUtils.isEmpty(riskBeanList)) {
                for (BpTeamSafeRiskBean bean : riskBeanList) {
                    bean.setSafeRiskId(null);
                    List<BpTeamSafeRiskDetailBean> detailBeanList = bean.getDetailBeanList();
                    if (!CollectionUtils.isEmpty(detailBeanList)) {
                        for (BpTeamSafeRiskDetailBean detailBean : detailBeanList) {
                            detailBean.setContentId(null);
                        }
                    }
                }
                return ResultBean.success(dto);
            } else {
                return null;
            }
        }
        return null;
    }

    @Override
    public ResultBean workCalculate(List<Long> teamIds) {
        List<BpTeam> list = bpTeamRepository.findAllById(teamIds);
        if (CollectionUtils.isEmpty(list)) {
            return ResultBean.defaultSuccessResult();
        }
        BpRiskDepartReportDto dto = new BpRiskDepartReportDto();
        if (!CollectionUtils.isEmpty(list)) {
            List<BpTeamSpecialBean> specialBeanList = Lists.newArrayList();
            list.parallelStream().forEach(r -> {
                List<BpTeamSpecialBean> beans = JSON.parseArray(r.getSpecialWork(), BpTeamSpecialBean.class);
                for (BpTeamSpecialBean bean : beans) {
                    specialBeanList.add(bean);
                }
            });
            List<BpTeamSpecialBean> resultList = Lists.newArrayList();
            Map<String, Integer> map = specialBeanList.stream().collect(Collectors.groupingBy(BpTeamSpecialBean::getSpecialWorkName, Collectors.summingInt(BpTeamSpecialBean::getSum)));
            for (String s : map.keySet()) {
                Integer sum = map.get(s);
                resultList.add(new BpTeamSpecialBean(s, sum));
            }
            dto.setSpecailBeanList(resultList);
        }
        return ResultBean.success(dto);
    }
}
